//
//  simple_timer.h
//
//  Created by Colin on 2019-08-05.
//  Copyright (c) 2019 Sumscope. All rights reserved.
//
#pragma once

#include <atomic>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <functional>
#include "../core.h"

#ifdef WIN32
#include <windows.h> // for ::Sleep() API
#endif

namespace qb
{
	class S_CORE_EXPORT simple_timer
	{
	public:
		simple_timer();
		simple_timer(const simple_timer& t);
		~simple_timer();

		void start(int interval, std::function<void()> task);

		/*
		* this is a asynchronization function, only notify simple_timer to stop, but
		* not wait simple_timer thread exit.
		*/
		void notify_stop();

		/*
		* this is a synchronization function, notify simple_timer to stop and wait
		* simple_timer thread finishing callback. so this function can't be called
		* in simple_timer callback function.
		*/
		void stop();

		template<typename callable, class... arguments>
		void sync_wait(int after, callable&& f, arguments&& ... args) 
		{
			std::function<typename std::result_of<callable(arguments...)>::type()>
			task(std::bind(std::forward<callable>(f), std::forward<arguments>(args)...));
#ifdef WIN32
			::Sleep(after);	// avoid sleep_until implementation bug.
#else
			std::this_thread::sleep_for(std::chrono::milliseconds(after));
#endif
			task();
		}

		template<typename callable, typename... arguments>
		void async_wait(int after, callable&& f, arguments&& ... args) 
		{
			std::function<typename std::result_of<callable(arguments...)>::type()> 
			task(std::bind(std::forward<callable>(f), std::forward<arguments>(args)...));

			std::thread([after, task]() 
			{
#ifdef WIN32
				::Sleep(after); // avoid sleep_until implementation bug.
#else
				std::this_thread::sleep_for(std::chrono::milliseconds(after));
#endif
				task();
			}).detach();
		}

	private:
		std::atomic<bool>		m_expired;
		std::atomic<bool>		m_try_to_expire;
		std::mutex				m_mutex;
		std::condition_variable m_expired_cond;
	};
}